﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

namespace EasyPhysic.Container
{
    /// <summary>
    /// 二位数据
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [NativeContainer]
    public unsafe struct NativeTwoDimensionalArray<T>  : IDisposable where T : unmanaged
    {

        private void** m_Buffer;
        internal Allocator m_AllocatorLabel;

        private int m_rows;
        private int m_cols;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // The AtomicSafetyHandle field must be named exactly 'm_Safety'.
        internal AtomicSafetyHandle m_Safety;
    
        // Statically register this type with the safety system, using a name derived from the type itself
        internal static readonly int s_staticSafetyId = AtomicSafetyHandle.NewStaticSafetyId<NativeTwoDimensionalArray<T>>();
#endif
        
        public NativeTwoDimensionalArray(Allocator allocator,int rows,int cols)
        {
            m_AllocatorLabel = allocator;

            m_rows = rows;
            m_cols = cols;
            // Calculate the size of the initial buffer in bytes, and allocate it
            int totalSize = rows * sizeof(T*) ;
            m_Buffer = (void**) UnsafeUtility.MallocTracked(totalSize,UnsafeUtility.AlignOf<T>(),m_AllocatorLabel,1);
            for (int i = 0; i < rows; i++)
            {
                
                m_Buffer[i] = UnsafeUtility.MallocTracked(cols, UnsafeUtility.AlignOf<T>(), m_AllocatorLabel, 1);
            }
           

            // // Copy the data from the array into the buffer
            // var handle = GCHandle.Alloc(initialItems, GCHandleType.Pinned);
            // try
            // {
            //     UnsafeUtility.MemCpy(m_Buffer, handle.AddrOfPinnedObject().ToPointer(), totalSize);
            // }
            // finally
            // {
            //     handle.Free();
            // }

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            // Create the AtomicSafetyHandle and DisposeSentinel
            m_Safety = AtomicSafetyHandle.Create();

            // Set the safety ID on the AtomicSafetyHandle so that error messages describe this container type properly.
            AtomicSafetyHandle.SetStaticSafetyId(ref m_Safety, s_staticSafetyId);
        
            // Automatically bump the secondary version any time this container is scheduled for writing in a job
            AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);

            // Check if this is a nested container, and if so, set the nested container flag
            if (UnsafeUtility.IsNativeContainerType<T>()) 
                AtomicSafetyHandle.SetNestedContainer(m_Safety, true);
#endif
        }
        public void Dispose()
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.CheckDeallocateAndThrow(m_Safety);
            AtomicSafetyHandle.Release(m_Safety);
#endif
            
            for (int i = 0; i < m_rows; i++)
            {
                UnsafeUtility.FreeTracked(m_Buffer[i], m_AllocatorLabel);
            }
            UnsafeUtility.FreeTracked(m_Buffer, m_AllocatorLabel);
            m_Buffer = null;
        }
    }
}